Skip to content

Fix/0.0.95 crashes#383

Merged
dishit-wednesday merged 6 commits into
mainfrom
fix/0.0.95-crashes
Jun 8, 2026
Merged

Fix/0.0.95 crashes#383
dishit-wednesday merged 6 commits into
mainfrom
fix/0.0.95-crashes

Conversation

@dishit-wednesday

Copy link
Copy Markdown
Collaborator

Summary

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Refactor (code change that neither fixes a bug nor adds a feature)
  • Chore (build process, CI, dependency updates, etc.)

Screenshots / Screen Recordings

Android

Before After

iOS

Before After

Checklist

General

  • My code follows the project's coding style and conventions
  • I have performed a self-review of my code
  • I have added/updated comments where the logic isn't self-evident
  • My changes generate no new warnings or errors

Testing

  • I have tested on Android (physical device or emulator)
  • I have tested on iOS (physical device or simulator)
  • I have tested in light mode and dark mode
  • Existing tests pass locally (npm test)
  • I have added tests that prove my fix is effective or my feature works

React Native Specific

  • No new native module without corresponding platform implementation (Android + iOS)
  • New native modules are added to the Xcode project build target (project.pbxproj)
  • No hardcoded pixel values — uses SPACING / TYPOGRAPHY constants from the theme
  • Styles use useThemedStyles pattern (not inline or static StyleSheet.create)
  • Animations/gestures work smoothly on both platforms
  • Large lists use FlatList / FlashList (not .map() inside ScrollView)
  • No unnecessary re-renders introduced (check with React DevTools Profiler if unsure)

Performance & Models

  • Downloads / long-running tasks report progress to the UI
  • File paths are resolved correctly on both platforms (no hardcoded / vs \\)
  • Large files (models, assets) are not committed to the repository

Security

  • No secrets, API keys, or credentials are included in the code
  • User input is validated/sanitized where applicable

Related Issues

Additional Notes

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.

@gemini-code-assist gemini-code-assist Bot left a comment

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.

Code Review

This pull request adds libvndksupport.so to the Android Manifest, implements a workaround to skip GPU initialization on Android 16 and Pixel 10 devices to prevent crashes, and adds a call to cancelProcess() before closing conversations to avoid a use-after-free SIGSEGV. The review feedback identifies critical race conditions and concurrency issues during conversation teardown, recommending the introduction of a Mutex to synchronize lifecycle operations and prevent native crashes.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

import android.util.Log
import android.app.ActivityManager
import android.content.Context
import android.os.Build

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.

critical

To prevent concurrent access and race conditions on the conversation and engine lifecycles, we should introduce a Mutex to synchronize these operations.

Suggested change
import android.os.Build
import android.os.Build
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
private val lifecycleMutex = Mutex()

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed differently — stopGeneration no longer calls closeConversationSafely at all. It now only signals stop synchronously: currentJob?.cancel() + conversation?.cancelProcess(). This eliminates the close/close race architecturally since only resetConversation and unloadModel ever call close(), and those are serialized via currentJob?.join() in closeConversationSafely.

The remaining concurrent-close case (two unloadModel calls racing) is handled by @Volatile on conversation plus a null-first swap: val conv = conversation ?: return; conversation = null. Only one caller gets the non-null handle and proceeds to close().

A Mutex here would also need to guard the conversation = eng.createConversation() assignment in resetConversation to be correct — otherwise a concurrent caller can still acquire the lock after close and later close a freshly created conversation.

Comment on lines 436 to 447
// Signal the native inference thread to stop before freeing the Conversation object.
// Without this, the native thread can fire onDone after close() frees the native handle,
// causing a use-after-free SIGSEGV in nativeSendMessageAsync.
try {
conversation?.cancelProcess()
} catch (e: Exception) {
Log.w(TAG, "closeConversationSafely — cancelProcess error: ${e.message}")
}

try {
conversation?.close()
Log.d(TAG, "closeConversationSafely — closed")

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.

critical

Critical Race Condition & Native Crash Hazard

Since closeConversationSafely is called from multiple coroutines running concurrently on Dispatchers.Default (e.g., during rapid user interaction, model switching, or stopping generation), there is a critical race condition:

  1. Concurrent Close / Use-After-Free: Multiple threads can call close() or cancelProcess() on the same native Conversation handle concurrently, leading to native SIGSEGV crashes.
  2. State Corruption: If resetConversation runs concurrently with closeConversationSafely, a newly created conversation can be overwritten with null in the finally block of a concurrent closeConversationSafely call, causing subsequent generation attempts to fail with LITERT_NO_CONV.

We should synchronize the conversation teardown using the lifecycleMutex and implement a double-checked locking pattern to ensure thread safety.

        // Signal the native inference thread to stop before freeing the Conversation object.
        // Without this, the native thread can fire onDone after close() frees the native handle,
        // causing a use-after-free SIGSEGV in nativeSendMessageAsync.
        try {
            lifecycleMutex.withLock {
                val conv = conversation
                if (conv != null) {
                    try {
                        conv.cancelProcess()
                    } catch (e: Exception) {
                        Log.w(TAG, "closeConversationSafely — cancelProcess error: ${e.message}")
                    }
                    conv.close()
                    Log.d(TAG, "closeConversationSafely — closed")
                }
            }

dishit-wednesday and others added 4 commits June 8, 2026 11:42
- Add libvndksupport.so to AndroidManifest to fix GPU init on Qualcomm SoCs
- Skip GPU backend on Pixel 10 where LiteRT GPU init crashes with
  uncatchable native abort (libPVROCL OOM/SIGSEGV)
- Call cancelProcess() before close() in closeConversationSafely to
  prevent use-after-free SIGSEGV in nativeSendMessageAsync

Co-Authored-By: Dishit Karia <hanmadishit74@gmail.com>
Add @volatile to conversation field so null-first swap in
closeConversationSafely is visible across threads; stopGeneration now
signals stop synchronously without tearing down the conversation,
eliminating the race with unloadModel that caused SIGSEGV

Co-Authored-By: Dishit Karia <hanmadishit74@gmail.com>
Co-Authored-By: Dishit Karia hanmadishit74@gmail.com
@codecov

codecov Bot commented Jun 8, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 0% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 81.82%. Comparing base (ca37944) to head (2698cbc).

Files with missing lines Patch % Lines
src/screens/ModelsScreen/TextModelsTab.tsx 0.00% 0 Missing and 1 partial ⚠️

❌ Your patch check has failed because the patch coverage (0.00%) is below the target coverage (80.00%). You can increase the patch coverage or adjust the target coverage.

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main     #383      +/-   ##
==========================================
- Coverage   81.82%   81.82%   -0.01%     
==========================================
  Files         241      241              
  Lines       12834    12835       +1     
  Branches     3534     3535       +1     
==========================================
  Hits        10502    10502              
  Misses       1403     1403              
- Partials      929      930       +1     
Files with missing lines Coverage Δ
src/screens/ModelsScreen/styles.ts 100.00% <ø> (ø)
src/screens/ModelsScreen/TextModelsTab.tsx 56.05% <0.00%> (-0.36%) ⬇️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

dishit-wednesday and others added 2 commits June 8, 2026 15:20
…GSEGV

finishRealtimeTranscribeJob dereferenced the result of job_get() without a
null check; when the job was already removed (double-finish on the 30s
buffer-full path, or an abort/transcribe race) this caused a SIGSEGV in
production (0.0.95). Add a null guard in jni.cpp and make the Java
finishRealtimeTranscribe a one-shot (synchronized + isRealtimeFinished)
so it cannot fire twice for the same job.

Co-Authored-By: Dishit Karia <hanmadishit74@gmail.com>
bring in one line with i button and change colour to yellow
@sonarqubecloud

sonarqubecloud Bot commented Jun 8, 2026

Copy link
Copy Markdown

@dishit-wednesday dishit-wednesday merged commit 7ac1d68 into main Jun 8, 2026
6 of 7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant