Skip to content

Commit

Permalink
Merge pull request #224 from embrace-io/nelson/semaphore-anr
Browse files Browse the repository at this point in the history
Update android faq.md with Semaphore.tryAcquire ANR
  • Loading branch information
nelsitoPuglisi authored Feb 28, 2025
2 parents c410cdf + c114d8e commit 80bb30c
Showing 1 changed file with 31 additions and 0 deletions.
31 changes: 31 additions & 0 deletions docs/android/faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,37 @@ Finally, SDKs use different approaches to capture and process crash data. For JV

All the reasons in this [crashes FAQ](#why-does-embraces-crash-data-look-different-compared-to-another-crash-reporting-solution-i-use) also applies to ANR data.

# Why am I seeing a `Semaphore.tryAcquire` ANR in `UnityPlayerActivity.onPause`?

This ANR (Application Not Responding) happens because a long-running operation in your game is blocking Unity’s ability to pause in a timely manner—it’s not caused by `OnApplicationPause` itself.

## What’s happening behind the scenes?

### Unity’s Android architecture
Every Unity Android app includes `UnityPlayerActivity`, a Java class that runs on the Android UI thread. It manages the app’s lifecycle and acts as a bridge between the OS and the Unity runtime, which runs on a separate thread.

### How pausing works
When the OS tells your app to pause (e.g., the user switches apps), that request goes to `UnityPlayerActivity.onPause` on the UI thread. To complete the pause process, the activity needs to notify the Unity engine (running on another thread) to trigger `OnApplicationPause` and suspend the game loop.

Since this requires coordination between threads, `onPause` must **wait** for a synchronization point at the end of Unity’s frame loop. This wait happens via `java.util.concurrent.Semaphore.tryAcquire`, which is what you see in the ANR stack trace.

### Why does this cause an ANR?
Normally, this wait lasts just a few milliseconds. However, if the Unity thread is already **blocked** by a long-running operation (such as synchronous asset loading or heavy computation), the pause request gets stuck waiting—leading to an ANR.

## Example scenario
Let’s say your game loads a large 3D environment when the user taps a button in the main menu. If the scene loads **synchronously** and takes several seconds (especially on a low-end device), the user usually just waits for it to complete. But if the OS happens to pause the app during this process (e.g., the user switches apps), the ongoing load delays the pause process, and boom—ANR.

## How to prevent this?

### Identify long-running synchronous operations
Use **performance tracing tools** to monitor scene and asset loads, SDK initializations, and queued async tasks that might be running on the Unity thread.

### Optimize your game loop
Where possible, move heavy operations **off the main Unity thread** or make them **asynchronous** to avoid blocking the pause request.

## Key takeaway
The `Semaphore.tryAcquire` ANR isn’t caused by `OnApplicationPause`—it’s caused by other long-running tasks in your game that prevent Unity from handling the pause in time. By reducing these blocking operations, you can avoid this issue and improve your app’s responsiveness.


## Integrating

Expand Down

0 comments on commit 80bb30c

Please sign in to comment.