Fix BGTask SIGSEGV from re-running php_embed_init under a live runtime#154
Merged
Conversation
A WorkManager scheduler job (PHPSchedulerWorker) firing while the app process is alive crashed in php_tsrm_startup_ex. The worker's initializeForBackground() runs runBaseArtisanCommands() on extraction, which routes through native_run_artisan_command — and that path called php_embed_init() unconditionally. With the persistent runtime / queue worker already holding a process-wide TSRM, the second php_embed_init() re-ran php_tsrm_startup_ex → zend_ini_refresh_caches → OnUpdateBool against live globals and null-deref'd. WorkManager then retry-looped it. - native_run_artisan_command now mirrors ephemeral_embed_init: wait for persistent boot to settle, then take a hot path (ts_resource(0) + module/request startup, teardown via php_request_shutdown + ts_free_thread) when a runtime is live, or the unchanged full php_embed_init cold path otherwise. Root-cause fix for every caller. - Add nativeIsPersistentRuntimeLive() JNI accessor (reads the process-wide persistent_initialized) so the background path can skip the install-time artisan commands the live app already ran at boot — those are MainActivity's responsibility. The C fix remains the backstop. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or 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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
A WorkManager scheduler job (
PHPSchedulerWorker) firing while the app process is alive SIGSEGVs inphp_tsrm_startup_ex. Tombstone:The worker's
initializeForBackground()runsrunBaseArtisanCommands()(optimize:clear,storage:unlink,storage:link,migrate --force) wheneverextractLaravelBundle()reports a change. Those route throughnative_run_artisan_command, which calledphp_embed_init()unconditionally. When the persistent runtime and/or the database queue worker already hold a process-wide TSRM context, the secondphp_embed_init()re-runsphp_tsrm_startup_ex → zend_ini_refresh_caches → OnUpdateBoolagainst live globals and null-derefs. WorkManager then retry-loops it (~2 min apart). It only stops if both the persistent runtime and the queue worker are disabled.In DEBUG builds (
NATIVEPHP_APP_VERSION="DEBUG") every extraction reports a change, so this fires on every background tick.Fix
native_run_artisan_commandis now TSRM-aware (php_bridge.c), mirroringephemeral_embed_init:wait_for_persistent_boot_settled()to avoid racing a mid-boot persistent thread;ts_resource(0)+php_embed_module.startup()+php_request_startup(), teardown viaphp_request_shutdown(NULL)+ts_free_thread()— leaving the global TSRM andphp_initializedintact;php_embed_init()/safe_php_embed_shutdown().This is the root-cause fix and makes the classic artisan path safe for every caller.
Warm-path guard — new
nativeIsPersistentRuntimeLive()JNI accessor (reads the process-widepersistent_initialized) letsinitializeForBackground()skip the install-time artisan commands when a persistent runtime is already live — those were already run by MainActivity at app boot. The C fix remains the backstop if the guard ever races a mid-boot runtime.Files
resources/androidstudio/app/src/main/cpp/php_bridge.c— hot/cold TSRM split + JNI accessor + registrationresources/androidstudio/app/src/main/java/com/nativephp/mobile/bridge/PHPBridge.kt—nativeIsPersistentRuntimeLivebindingresources/androidstudio/app/src/main/java/com/nativephp/mobile/bridge/LaravelEnvironment.kt— guard ininitializeForBackground()Testing
Verified on an Android emulator with a DEBUG build,
runtime_mode: persistent, andQUEUE_CONNECTION=database: firing a scheduled background task while the app is open no longer SIGSEGVs; logcat shows⏭️ Skipping base artisan commands — persistent runtime already live. Reverting the fix reproducesFatal signal 11 (SIGSEGV)inphp_tsrm_startup.iOS is unaffected — its scheduler already routes background work through
ephemeral_php_boot/ephemeral_embed_init, which attaches to the existing TSRM.🤖 Generated with Claude Code