Skip to content

Fix potential ANR by making FaceRecognitionHelper.close non-blocking#160

Closed
harrydbarnes wants to merge 1 commit into
mainfrom
fix-anr-face-recognition-cleanup-10987144735960004022
Closed

Fix potential ANR by making FaceRecognitionHelper.close non-blocking#160
harrydbarnes wants to merge 1 commit into
mainfrom
fix-anr-face-recognition-cleanup-10987144735960004022

Conversation

@harrydbarnes

Copy link
Copy Markdown
Owner

Refactored FaceRecognitionHelper.close() to execute cleanup operations (TFLite interpreter and GPU delegate closure) asynchronously on Dispatchers.IO instead of using runBlocking on the calling thread. This prevents main-thread blocking and potential ANRs when ProjectViewModel cleans up resources while a long-running inference might be holding the mutex.

Changes:

  • Removed runBlocking in close().
  • Launched a coroutine on Dispatchers.IO to acquire the lock and close resources.
  • Added necessary imports (CoroutineScope, Dispatchers, launch).

Refactored `FaceRecognitionHelper.close()` to execute cleanup operations (TFLite interpreter and GPU delegate closure) asynchronously on `Dispatchers.IO` instead of using `runBlocking` on the calling thread. This prevents main-thread blocking and potential ANRs when `ProjectViewModel` cleans up resources while a long-running inference might be holding the mutex.

Changes:
- Removed `runBlocking` in `close()`.
- Launched a coroutine on `Dispatchers.IO` to acquire the lock and close resources.
- Added necessary imports (`CoroutineScope`, `Dispatchers`, `launch`).

Co-authored-by: harrydbarnes <145344818+harrydbarnes@users.noreply.github.com>
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello @harrydbarnes, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request addresses a potential ANR issue by modifying the FaceRecognitionHelper.close() method. Previously, this method used runBlocking to perform resource cleanup, which could block the main thread and lead to ANRs. The update transitions this operation to an asynchronous coroutine launched on Dispatchers.IO, ensuring that resource deallocation occurs off the main thread, thereby improving application responsiveness and stability.

Highlights

  • Asynchronous Resource Cleanup: The FaceRecognitionHelper.close() method has been refactored to perform cleanup operations (TFLite interpreter and GPU delegate closure) asynchronously.
  • ANR Prevention: This change prevents potential Application Not Responding (ANR) errors by ensuring that resource cleanup does not block the calling thread, especially when called from the main thread.
  • Coroutine Usage: The runBlocking call has been replaced with a CoroutineScope(Dispatchers.IO).launch block, moving the potentially long-running cleanup to a background I/O thread.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@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 effectively resolves a potential Application Not Responding (ANR) issue by refactoring the FaceRecognitionHelper.close() method to be non-blocking. The replacement of runBlocking with an asynchronous coroutine launch is a solid improvement. I have one suggestion to further enhance the implementation by aligning it with structured concurrency best practices for managing coroutine scopes.


override fun close() {
runBlocking {
CoroutineScope(Dispatchers.IO).launch {

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.

medium

While this change correctly makes the close operation non-blocking, creating a new CoroutineScope on each call is not ideal. It's a better practice for a class that launches coroutines to own and manage a single CoroutineScope throughout its lifecycle. This avoids the overhead of creating new scopes and aligns with structured concurrency principles.

A more robust approach would be to define a scope as a property of the FaceRecognitionHelper class.

Example:

// At the class level, with other properties
private val helperScope = CoroutineScope(Dispatchers.IO + SupervisorJob())

// Then, in your close() method
helperScope.launch { ... }

This would centralize coroutine management and make the helper's asynchronous operations more manageable and efficient.

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