Skip to content

Termux-Ausgabe: Wechsel von apply() zu commit() zur Vermeidung einer Race-Condition#105

Open
Android-PowerUser wants to merge 4 commits into
fix-termux-command-execution-issuesfrom
investigate-termux-output-handling-issue
Open

Termux-Ausgabe: Wechsel von apply() zu commit() zur Vermeidung einer Race-Condition#105
Android-PowerUser wants to merge 4 commits into
fix-termux-command-execution-issuesfrom
investigate-termux-output-handling-issue

Conversation

@Android-PowerUser
Copy link
Copy Markdown
Owner

Motivation

  • Termux-Callback-Daten konnten verloren gehen, weil SharedPreferences.apply() asynchron schreibt und die nächste "Screen elements"-Bubble vor dem Persistieren lesen kann, wodurch die Termux-Ausgabe nicht injiziert wird.

Description

  • In app/src/main/kotlin/com/google/ai/sample/util/TermuxOutputPreferences.kt wurde appendOutput so geändert, dass es SharedPreferences.edit().putString(...).commit() statt apply() verwendet.
  • Ebenso wurde consumeOutput so geändert, dass das Entfernen mit commit() statt apply() erfolgt.
  • Bei fehlschlagendem commit() wird nun eine IllegalStateException geworfen, um stille Persistenzfehler sichtbar zu machen.

Testing

  • Lint ausgeführt mit ./gradlew :app:lintDebug — fehlgeschlagen wegen bestehender, repo-weiter Lint-Fehler (z.B. ProtectedPermissions in AndroidManifest.xml), die nicht durch diesen Patch verursacht wurden.
  • Kotlin-Compile ausgeführt mit ./gradlew :app:compileDebugKotlin — erfolgreich.

Codex Task

Copy link
Copy Markdown
Contributor

@amazon-q-developer amazon-q-developer Bot left a comment

Choose a reason for hiding this comment

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

Summary

This PR attempts to fix a race condition by switching from apply() to commit() in SharedPreferences operations. While the race condition concern is valid, the implementation introduces critical risks that block merge:

Critical Issues Found

  1. ANR Risk: The synchronous commit() calls perform blocking I/O on the calling thread. If these methods are invoked from the main thread, this will freeze the UI and may trigger Application Not Responding (ANR) dialogs.

  2. Crash on Storage Failures: Throwing IllegalStateException when commit() returns false will crash the app. The method can legitimately fail in non-exceptional scenarios (disk full, permissions issues, device shutdown), trading silent data loss for guaranteed crashes.

Recommended Approach

Consider one of these alternatives:

  • Move the commit() calls to a background thread/coroutine to avoid blocking the UI
  • Use proper synchronization mechanisms with apply() (e.g., locks or atomic operations)
  • Handle commit failures gracefully with logging and error indicators rather than crashing
  • Evaluate if the race condition frequency justifies the performance trade-off

The current implementation replaces a potential data loss issue with guaranteed UI freezing and app crashes, which is a regression.


You can now have the agent implement changes and create commits directly on your pull request's source branch. Simply comment with /q followed by your request in natural language to ask the agent to make changes.

val existing = prefs.getString(KEY_PENDING_OUTPUT, "").orEmpty()
val merged = if (existing.isBlank()) output else "$existing\n\n$output"
prefs.edit().putString(KEY_PENDING_OUTPUT, merged).apply()
val committed = prefs.edit().putString(KEY_PENDING_OUTPUT, merged).commit()
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛑 Crash Risk: The synchronous commit() call performs blocking I/O on the calling thread. If appendOutput is called from the main thread, this will freeze the UI and may trigger ANR (Application Not Responding) dialogs, degrading user experience. Consider using a background thread with commit() or keeping apply() with proper synchronization mechanisms to avoid the race condition without blocking the UI thread.

Comment on lines +15 to +17
if (!committed) {
throw IllegalStateException("Failed to persist pending Termux output")
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛑 Crash Risk: Throwing IllegalStateException on commit failure will crash the app. commit() can legitimately return false in non-exceptional scenarios (disk full, storage permissions issues, device shutting down). This replaces a silent data loss issue with guaranteed app crashes. Consider logging the error and returning a failure indicator instead, or using an alternative synchronization strategy that doesn't crash on storage failures.

val value = prefs.getString(KEY_PENDING_OUTPUT, "").orEmpty().trim()
if (value.isBlank()) return null
prefs.edit().remove(KEY_PENDING_OUTPUT).apply()
val committed = prefs.edit().remove(KEY_PENDING_OUTPUT).commit()
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛑 Crash Risk: The synchronous commit() call performs blocking I/O on the calling thread. If consumeOutput is called from the main thread, this will freeze the UI and may trigger ANR (Application Not Responding) dialogs. Consider using a background thread with commit() or an alternative approach that doesn't block the UI thread.

Comment on lines +25 to +27
if (!committed) {
throw IllegalStateException("Failed to clear consumed Termux output")
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛑 Crash Risk: Throwing IllegalStateException on commit failure will crash the app instead of handling storage errors gracefully. commit() can return false in legitimate scenarios like disk full or permissions issues. This trades silent data loss for guaranteed crashes. Consider logging the error and handling it gracefully, or returning a failure indicator to the caller.

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant