-
Notifications
You must be signed in to change notification settings - Fork 24.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Complete conversion of modules.debug module (#44476)
Summary: Pull Request resolved: #44476 # Changelog: [Internal] - This is the last step to converting the whole `modules.debug` module to Kotlin. Reviewed By: christophpurrer Differential Revision: D57095966 fbshipit-source-id: 3fcb52528674565a4a2b5c306262e0af11a19e6e
- Loading branch information
1 parent
7ec70a9
commit 7835aa4
Showing
5 changed files
with
186 additions
and
210 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
169 changes: 0 additions & 169 deletions
169
...roid/src/main/java/com/facebook/react/modules/debug/DidJSUpdateUiDuringFrameDetector.java
This file was deleted.
Oops, something went wrong.
158 changes: 158 additions & 0 deletions
158
...ndroid/src/main/java/com/facebook/react/modules/debug/DidJSUpdateUiDuringFrameDetector.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
/* | ||
* Copyright (c) Meta Platforms, Inc. and affiliates. | ||
* | ||
* This source code is licensed under the MIT license found in the | ||
* LICENSE file in the root directory of this source tree. | ||
*/ | ||
|
||
package com.facebook.react.modules.debug | ||
|
||
import com.facebook.react.bridge.NotThreadSafeBridgeIdleDebugListener | ||
import com.facebook.react.common.LongArray | ||
import com.facebook.react.uimanager.debug.NotThreadSafeViewHierarchyUpdateDebugListener | ||
|
||
/** | ||
* Debug object that listens to bridge busy/idle events and UiManagerModule dispatches and uses it | ||
* to calculate whether JS was able to update the UI during a given frame. After being installed on | ||
* a [ReactBridge] and a [UIManagerModule], [.getDidJSHitFrameAndCleanup] should be called once per | ||
* frame via a [Choreographer.FrameCallback]. | ||
*/ | ||
internal class DidJSUpdateUiDuringFrameDetector : | ||
NotThreadSafeBridgeIdleDebugListener, NotThreadSafeViewHierarchyUpdateDebugListener { | ||
private val transitionToIdleEvents = LongArray.createWithInitialCapacity(20) | ||
private val transitionToBusyEvents = LongArray.createWithInitialCapacity(20) | ||
private val viewHierarchyUpdateEnqueuedEvents = LongArray.createWithInitialCapacity(20) | ||
private val viewHierarchyUpdateFinishedEvents = LongArray.createWithInitialCapacity(20) | ||
@Volatile private var wasIdleAtEndOfLastFrame = true | ||
|
||
@Synchronized | ||
override fun onTransitionToBridgeIdle() { | ||
transitionToIdleEvents.add(System.nanoTime()) | ||
} | ||
|
||
@Synchronized | ||
override fun onTransitionToBridgeBusy() { | ||
transitionToBusyEvents.add(System.nanoTime()) | ||
} | ||
|
||
@Synchronized | ||
override fun onBridgeDestroyed() { | ||
// do nothing | ||
} | ||
|
||
@Synchronized | ||
override fun onViewHierarchyUpdateEnqueued() { | ||
viewHierarchyUpdateEnqueuedEvents.add(System.nanoTime()) | ||
} | ||
|
||
@Synchronized | ||
override fun onViewHierarchyUpdateFinished() { | ||
viewHierarchyUpdateFinishedEvents.add(System.nanoTime()) | ||
} | ||
|
||
/** | ||
* Designed to be called from a [Choreographer.FrameCallback.doFrame] call. | ||
* | ||
* There are two 'success' cases that will cause [.getDidJSHitFrameAndCleanup] to return true for | ||
* a given frame: | ||
* 1. UIManagerModule finished dispatching a batched UI update on the UI thread during the frame. | ||
* This means that during the next hierarchy traversal, new UI will be drawn if needed (good). | ||
* 1. The bridge ended the frame idle (meaning there were no JS nor native module calls still in | ||
* flight) AND there was no UiManagerModule update enqueued that didn't also finish. NB: if | ||
* there was one enqueued that actually finished, we'd have case 1), so effectively we just | ||
* look for whether one was enqueued. | ||
* | ||
* NB: This call can only be called once for a given frame time range because it cleans up events | ||
* it recorded for that frame. | ||
* | ||
* NB2: This makes the assumption that onViewHierarchyUpdateEnqueued is called from the | ||
* [ ][UIManagerModule.onBatchComplete], e.g. while the bridge is still considered busy, which | ||
* means there is no race condition where the bridge has gone idle but a hierarchy update is | ||
* waiting to be enqueued. | ||
* | ||
* @param frameStartTimeNanos the time in nanos that the last frame started | ||
* @param frameEndTimeNanos the time in nanos that the last frame ended | ||
*/ | ||
@Synchronized | ||
fun getDidJSHitFrameAndCleanup(frameStartTimeNanos: Long, frameEndTimeNanos: Long): Boolean { | ||
// Case 1: We dispatched a UI update | ||
val finishedUiUpdate = | ||
hasEventBetweenTimestamps( | ||
viewHierarchyUpdateFinishedEvents, frameStartTimeNanos, frameEndTimeNanos) | ||
val didEndFrameIdle = didEndFrameIdle(frameStartTimeNanos, frameEndTimeNanos) | ||
val hitFrame = | ||
if (finishedUiUpdate) { | ||
true | ||
} else { | ||
// Case 2: Ended idle but no UI was enqueued during that frame | ||
(didEndFrameIdle && | ||
!hasEventBetweenTimestamps( | ||
viewHierarchyUpdateEnqueuedEvents, frameStartTimeNanos, frameEndTimeNanos)) | ||
} | ||
cleanUp(transitionToIdleEvents, frameEndTimeNanos) | ||
cleanUp(transitionToBusyEvents, frameEndTimeNanos) | ||
cleanUp(viewHierarchyUpdateEnqueuedEvents, frameEndTimeNanos) | ||
cleanUp(viewHierarchyUpdateFinishedEvents, frameEndTimeNanos) | ||
wasIdleAtEndOfLastFrame = didEndFrameIdle | ||
return hitFrame | ||
} | ||
|
||
private fun didEndFrameIdle(startTime: Long, endTime: Long): Boolean { | ||
val lastIdleTransition = | ||
getLastEventBetweenTimestamps(transitionToIdleEvents, startTime, endTime) | ||
val lastBusyTransition = | ||
getLastEventBetweenTimestamps(transitionToBusyEvents, startTime, endTime) | ||
return if (lastIdleTransition == -1L && lastBusyTransition == -1L) { | ||
wasIdleAtEndOfLastFrame | ||
} else lastIdleTransition > lastBusyTransition | ||
} | ||
|
||
companion object { | ||
private fun hasEventBetweenTimestamps( | ||
eventArray: LongArray, | ||
startTime: Long, | ||
endTime: Long | ||
): Boolean { | ||
for (i in 0 until eventArray.size()) { | ||
val time = eventArray[i] | ||
if (time in startTime until endTime) { | ||
return true | ||
} | ||
} | ||
return false | ||
} | ||
|
||
private fun getLastEventBetweenTimestamps( | ||
eventArray: LongArray, | ||
startTime: Long, | ||
endTime: Long | ||
): Long { | ||
var lastEvent: Long = -1 | ||
for (i in 0 until eventArray.size()) { | ||
val time = eventArray[i] | ||
if (time in startTime until endTime) { | ||
lastEvent = time | ||
} else if (time >= endTime) { | ||
break | ||
} | ||
} | ||
return lastEvent | ||
} | ||
|
||
private fun cleanUp(eventArray: LongArray, endTime: Long) { | ||
val size = eventArray.size() | ||
var indicesToRemove = 0 | ||
for (i in 0 until size) { | ||
if (eventArray[i] < endTime) { | ||
indicesToRemove++ | ||
} | ||
} | ||
if (indicesToRemove > 0) { | ||
for (i in 0 until size - indicesToRemove) { | ||
eventArray[i] = eventArray[i + indicesToRemove] | ||
} | ||
eventArray.dropTail(indicesToRemove) | ||
} | ||
} | ||
} | ||
} |
39 changes: 0 additions & 39 deletions
39
...-native/ReactAndroid/src/main/java/com/facebook/react/modules/debug/SourceCodeModule.java
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.