fix: make render orchestrator teardown safe
Some checks failed
validate-scripts / validate-scripts (push) Has been cancelled

This commit is contained in:
tei
2026-10-07 08:41:08 +09:00
parent d29e84c1a9
commit 64cf8f4435
13 changed files with 1483 additions and 6 deletions

View File

@@ -62,6 +62,79 @@ class ParticleCoreLayerValidationActivity : AppCompatActivity() {
private var step093Mode = false
private var step097Topology: Step097StencilFixture.Topology? = null
private var step099Scenario: Step099ReadinessFixture.Scenario? = null
private var step101Scenario: String? = null
private var step101Completed = 0
private var step101DynamicTick = 0L
private var step101ReadySinceMs = 0L
private var step101LoadSinceMs = 0L
private val step101Handler = Handler(Looper.getMainLooper())
private val step101Poll = object : Runnable {
override fun run() {
val scenario = step101Scenario ?: return
if (!this@ParticleCoreLayerValidationActivity::maplibreMap.isInitialized || !styleReady) {
if (SystemClock.elapsedRealtime() - step101LoadSinceMs > 20000L) {
Log.e(TAG, "STEP101_SCENARIO_BLOCKED scenario=$scenario completed=$step101Completed reason=style-not-ready")
status.text = "STEP-101 BLOCKED: style not ready"
step101Scenario = null
return
}
step101Handler.postDelayed(this, 250L)
return
}
val snapshot = JSONObject(maplibreMap.getRenderSegmentDiagnostics())
val sources = snapshot.optJSONArray("sources")
var tileReady = false
for (index in 0 until (sources?.length() ?: 0)) {
val source = sources?.optJSONObject(index) ?: continue
if (source.optString("sourceId") == Step099ReadinessFixture.STATIC_SOURCE &&
source.optBoolean("isLoaded") && source.optInt("tileCount") > 0) tileReady = true
}
if (!tileReady) {
if (SystemClock.elapsedRealtime() - step101LoadSinceMs > 20000L) {
Log.e(TAG, "STEP101_SCENARIO_BLOCKED scenario=$scenario completed=$step101Completed reason=tile-not-ready")
status.text = "STEP-101 BLOCKED: tile not ready"
step101Scenario = null
return
}
step101Handler.postDelayed(this, 250L)
return
}
if (step101ReadySinceMs == 0L) {
step101ReadySinceMs = SystemClock.elapsedRealtime()
Log.i(TAG, "STEP101_SOURCE_TILE_READY scenario=$scenario completed=$step101Completed " +
"sources=${sources?.length()} diagnostics=${snapshot.optLong("contentRevision")}")
}
if (scenario == "T3_RESET_WITH_DYNAMIC_GEOJSON") {
step101DynamicTick += 1
updateStep099GeoJson(Step099ReadinessFixture.DYNAMIC_SOURCE, step101DynamicTick)
Log.i(TAG, "STEP101_DYNAMIC_SOURCE_MUTATION scenario=$scenario completed=$step101Completed " +
"tick=$step101DynamicTick")
}
if (SystemClock.elapsedRealtime() - step101ReadySinceMs < 2500L) {
step101Handler.postDelayed(this, 250L)
return
}
Log.i(TAG, "STEP101_NEW_RENDERER_READY scenario=$scenario completed=$step101Completed " +
"styleReady=$styleReady diagnostics=${snapshot.optLong("contentRevision")}")
val target = if (scenario == "T2_RESET_LOOP_20") 20 else 1
if (step101Completed >= target) {
Log.i(TAG, "STEP101_SCENARIO_DONE scenario=$scenario resetsRequested=$step101Completed " +
"nativeResetPairsMustMatch=$step101Completed")
status.text = "STEP-101 $scenario DONE ${step101Completed}/$target"
step101Scenario = null
return
}
if (scenario == "T4_RESET_WITH_DIAGNOSTICS") {
Log.i(TAG, "STEP101_PRE_RESET_DIAGNOSTICS ${maplibreMap.getRenderSegmentDiagnostics()}")
}
val next = step101Completed + 1
Log.i(TAG, "STEP101_RESET_REQUEST scenario=$scenario iteration=$next oldMapViewActive=true " +
"tileReady=$tileReady")
step101Completed = next
step101Handler.removeCallbacks(this)
recreate() // Activity destroys the old MapView and constructs a new native renderer.
}
}
private var step099Tick = 0L
private var step099LastOverallLoaded: Boolean? = null
private var step099FalseSinceMs: Long? = null
@@ -189,6 +262,12 @@ class ParticleCoreLayerValidationActivity : AppCompatActivity() {
step093Mode = savedInstanceState?.getBoolean(STATE_STEP093_MODE, false) ?: false
step097Topology = savedInstanceState?.getString(STATE_STEP097_TOPOLOGY)
?.let { value -> Step097StencilFixture.Topology.values().firstOrNull { it.name == value } }
step101Scenario = if (savedInstanceState?.containsKey(STATE_STEP101_SCENARIO) == true) {
savedInstanceState.getString(STATE_STEP101_SCENARIO)
} else {
intent.getStringExtra("step101_scenario")?.takeIf { it in STEP101_SCENARIOS }
}
step101Completed = savedInstanceState?.getInt(STATE_STEP101_COMPLETED, 0) ?: 0
step093Topology = savedInstanceState?.getString(STATE_STEP093_TOPOLOGY)
?.let { value -> Step093Topology.values().firstOrNull { it.name == value } }
?: Step093Topology.BOTTOM_CONTINUOUS_TOP
@@ -227,7 +306,8 @@ class ParticleCoreLayerValidationActivity : AppCompatActivity() {
updateNavSeaPbfButtonState()
updatePbfModeButtonState()
updateRetainedCacheButtonState()
if (step097Topology != null) loadStep097StencilStyle()
if (step101Scenario != null) loadStep101Style()
else if (step097Topology != null) loadStep097StencilStyle()
else if (step093Mode) loadStep093Style(configureBeforeStyle = false)
else loadValidationStyle(active = active, enabled = enabled, reason = "INITIAL")
}
@@ -532,6 +612,7 @@ class ParticleCoreLayerValidationActivity : AppCompatActivity() {
menu.add(0, 15, 15, "RESET DEFAULT A")
menu.add(0, 16, 16, "STEP-097 SAME SOURCE STENCIL")
menu.add(0, 17, 17, "STEP-099 READINESS")
menu.add(0, 18, 18, "STEP-101 TEARDOWN")
setOnMenuItemClickListener { item ->
when (item.itemId) {
in Step093Topology.values().indices -> applyStep093Scene(Step093Topology.values()[item.itemId])
@@ -543,6 +624,7 @@ class ParticleCoreLayerValidationActivity : AppCompatActivity() {
15 -> applyStep093Scene(Step093Topology.BOTTOM_CONTINUOUS_TOP)
16 -> showStep097StencilMenu(anchor)
17 -> showStep099Menu(anchor)
18 -> showStep101Menu(anchor)
}
true
}
@@ -589,6 +671,45 @@ class ParticleCoreLayerValidationActivity : AppCompatActivity() {
}
}
private fun showStep101Menu(anchor: android.view.View) {
PopupMenu(this, anchor).apply {
STEP101_SCENARIOS.forEachIndexed { index, scenario -> menu.add(0, index, index, scenario) }
setOnMenuItemClickListener { item ->
val scenario = STEP101_SCENARIOS.getOrNull(item.itemId) ?: return@setOnMenuItemClickListener false
step101Scenario = scenario
step101Completed = 0
loadStep101Style()
true
}
show()
}
}
private fun loadStep101Style() {
val scenario = step101Scenario ?: return
stopStep099Scenario()
step101Handler.removeCallbacksAndMessages(null)
step101ReadySinceMs = 0L
step101LoadSinceMs = SystemClock.elapsedRealtime()
val fixture = Step099ReadinessFixture.Scenario.T4_RCR
val accepted = maplibreMap.setRenderSegmentPlan(Step099ReadinessFixture.planJson(fixture))
styleReady = false
val epoch = ++styleLoadEpoch
Log.i(TAG, "STEP101_OLD_RESET_COMPLETED_OR_INITIAL scenario=$scenario completed=$step101Completed " +
"planAccepted=$accepted")
maplibreMap.setStyle(Style.Builder().fromJson(Step099ReadinessFixture.styleJson(fixture))) {
if (step101Scenario != scenario || epoch != styleLoadEpoch) return@setStyle
styleReady = true
maplibreMap.moveCamera(CameraUpdateFactory.newCameraPosition(
CameraPosition.Builder().target(LatLng(28.0, 135.0)).zoom(3.0).build()
))
Log.i(TAG, "STEP101_STYLE_READY scenario=$scenario completed=$step101Completed")
step101Handler.removeCallbacks(step101Poll)
step101Handler.post(step101Poll)
}
step101Handler.post(step101Poll)
}
private fun applyStep099Scenario(scenario: Step099ReadinessFixture.Scenario) {
stopStep093DiagnosticsPolling()
step093Mode = false
@@ -2135,6 +2256,7 @@ class ParticleCoreLayerValidationActivity : AppCompatActivity() {
override fun onStart() {
super.onStart()
mapView.onStart()
if (step101Scenario != null && styleReady) step101Handler.post(step101Poll)
if (step099Scenario != null) {
step099Running = true
step099LastOverallLoaded = null
@@ -2149,6 +2271,7 @@ class ParticleCoreLayerValidationActivity : AppCompatActivity() {
override fun onResume() { super.onResume(); mapView.onResume() }
override fun onPause() { mapView.onPause(); super.onPause() }
override fun onStop() {
step101Handler.removeCallbacksAndMessages(null)
step093Handler.removeCallbacks(step093DiagnosticsPoll)
step099Running = false
step099Handler.removeCallbacksAndMessages(null)
@@ -2156,11 +2279,14 @@ class ParticleCoreLayerValidationActivity : AppCompatActivity() {
super.onStop()
}
override fun onDestroy() {
step101Handler.removeCallbacksAndMessages(null)
if (step101Scenario != null) Log.i(TAG, "STEP101_MAPVIEW_DESTROY_ENTER scenario=$step101Scenario completed=$step101Completed")
step093Handler.removeCallbacks(step093DiagnosticsPoll)
stopStep099Scenario()
weatherRuntime?.dispose()
weatherRuntime = null
mapView.onDestroy()
if (step101Scenario != null) Log.i(TAG, "STEP101_MAPVIEW_DESTROY_RETURN scenario=$step101Scenario completed=$step101Completed")
super.onDestroy()
}
override fun onLowMemory() { super.onLowMemory(); mapView.onLowMemory() }
@@ -2189,6 +2315,8 @@ class ParticleCoreLayerValidationActivity : AppCompatActivity() {
outState.putBoolean(STATE_RETAINED_CACHE_ENABLED, retainedCacheEnabled)
outState.putBoolean(STATE_STEP093_MODE, step093Mode)
outState.putString(STATE_STEP097_TOPOLOGY, step097Topology?.name)
outState.putString(STATE_STEP101_SCENARIO, step101Scenario)
outState.putInt(STATE_STEP101_COMPLETED, step101Completed)
outState.putString(STATE_STEP093_TOPOLOGY, step093Topology.name)
outState.putString(STATE_STEP093_VARIANT, step093Variant.name)
super.onSaveInstanceState(outState)
@@ -2226,6 +2354,11 @@ class ParticleCoreLayerValidationActivity : AppCompatActivity() {
private const val STATE_RETAINED_CACHE_ENABLED = "particle_validation.retained_cache_enabled"
private const val STATE_STEP093_MODE = "particle_validation.step093_mode"
private const val STATE_STEP097_TOPOLOGY = "particle_validation.step097_topology"
private const val STATE_STEP101_SCENARIO = "particle_validation.step101_scenario"
private const val STATE_STEP101_COMPLETED = "particle_validation.step101_completed"
private val STEP101_SCENARIOS = listOf(
"T1_RESET_ONCE", "T2_RESET_LOOP_20", "T3_RESET_WITH_DYNAMIC_GEOJSON", "T4_RESET_WITH_DIAGNOSTICS"
)
private const val STATE_STEP093_TOPOLOGY = "particle_validation.step093_topology"
private const val STATE_STEP093_VARIANT = "particle_validation.step093_variant"
private const val PARTICLE_LAYER_ID = "particle-raster-core"