Deadlock/Freezing Issue with LocalizationSettings.StringDatabase.GetLocalizedString Under Multiple Concurrent Calls on Mobile

Environment:

• Unity Version: 2022.3.26f1

• Localization Package Version: 1.4.5

• Platform: Android (IL2CPP Build)

• Issue does not occur on PC standalone or in Editor play mode.

Summary:

When using LocalizationSettings.StringDatabase.GetLocalizedString multiple times in rapid succession (particularly in error handling or retry loops) on Android (IL2CPP build), the application appears to freeze or become unresponsive. This issue does not manifest on PC builds or in the Unity Editor. Replacing GetLocalizedString calls with GetLocalizedStringAsync resolves the freezing issue, suggesting that the synchronous variant may be causing a deadlock or indefinite wait state under certain conditions on mobile.

Steps to Reproduce:

  1. Set up a Unity project with Localization 1.4.5 and Addressables-based localization tables.

  2. Implement logic that triggers multiple localization string retrievals simultaneously (e.g., retrieving multiple localized strings for UI labels after a network error).

  3. Build and run the project on an Android device using IL2CPP scripting backend.

  4. Trigger the condition where multiple GetLocalizedString calls occur close together (e.g., after a network request failure).

  5. Observe that the application often freezes or becomes unresponsive, showing no further logs or UI updates.

Expected Behavior:

LocalizationSettings.StringDatabase.GetLocalizedString should return localized strings or handle resource loading gracefully without causing deadlocks or indefinite waits, even when called multiple times in a short period. The application should remain responsive.

Actual Behavior:

On Android IL2CPP builds, when GetLocalizedString is invoked multiple times concurrently, the application may freeze. In contrast, switching to await LocalizationSettings.StringDatabase.GetLocalizedStringAsync for all such calls allows the application to remain stable and responsive under the same conditions.

Workarounds Confirmed:

• Using GetLocalizedStringAsync instead of GetLocalizedString eliminates the freezing.

• Reducing concurrent calls or ensuring that the Localization system is fully initialized before calling GetLocalizedString multiple times can reduce the frequency of the freeze.

Additional Notes:

• The issue is specific to mobile (Android) IL2CPP builds. The same code path works fine on PC standalone builds and in the Unity Editor.

• The problem seems related to the internal async loading of localization tables or resources. The synchronous call may be entering a state where it indefinitely waits for resources that never finalize loading, especially under multiple simultaneous calls.

Below is a simplified code example demonstrating the issue. In this scenario, multiple localized strings are requested nearly simultaneously using UniTask.WhenAll. On Android IL2CPP builds, this can lead to the application freezing, whereas on PC standalone or in the Editor it works fine.

Example Code:

using System;
using System.Collections.Generic;
using Cysharp.Threading.Tasks;
using UnityEngine;
using UnityEngine.Localization.Settings;

public static class NetworkErrorHandler
{
    private const int MaxRetries = 3;
    private const int DelayBetweenRetriesMs = 2000;

    public static async UniTask<T> HandleRequestAsync<T>(Func<UniTask<T>> requestFunc)
    {
        int retryCount = 0;

        while (true)
        {
            try
            {
                // Attempt the network request
                var result = await requestFunc();
                Debug.Log("Request succeeded.");
                return result;
            }
            catch (Exception ex)
            {
                Debug.LogError($"Request failed: {ex.Message}");
                retryCount++;

                if (retryCount >= MaxRetries)
                {
                    Debug.LogError("Max retries reached. Retrieving localized error messages...");

                    // Retrieve localized error messages
                    var title = LocalizationSettings.StringDatabase.GetLocalizedString("NetworkError_Title");
                    var description = LocalizationSettings.StringDatabase.GetLocalizedString("NetworkError_Description");
                    var retryButton = LocalizationSettings.StringDatabase.GetLocalizedString("NetworkError_RetryButton");

                    // Log the localized messages
                    Debug.Log($"Localized Title: {title}");
                    Debug.Log($"Localized Description: {description}");
                    Debug.Log($"Localized Retry Button: {retryButton}");

                    throw; // Propagate the exception
                }

                Debug.Log($"Retrying in {DelayBetweenRetriesMs / 1000} seconds...");
                await UniTask.Delay(DelayBetweenRetriesMs);
            }
        }
    }
}

public class GenericWebAPIManager
{
    public async UniTask<string> RequestDataAsync(string endpoint)
    {
        // Simulate a failing network request
        await UniTask.Yield();
        
        // Randomly simulate success or failure
        if (UnityEngine.Random.value < 0.5f)
        {
            throw new Exception($"Simulated network failure on endpoint: {endpoint}");
        }

        return $"Data from {endpoint}";
    }

    public UniTask<string> RunRequestWithErrorHandlingAsync(string endpoint)
    {
        // Handle the network request with retry and error handling
        return NetworkErrorHandler.HandleRequestAsync(() => RequestDataAsync(endpoint));
    }
}

public class MultipleRequestRunner : MonoBehaviour
{
    private async void Start()
    {
        var apiManager = new GenericWebAPIManager();

        // Simulate multiple endpoints being called concurrently
        var endpoints = new[] { "Endpoint1", "Endpoint2", "Endpoint3" };
        var tasks = new UniTask<string>[endpoints.Length];

        for (int i = 0; i < endpoints.Length; i++)
        {
            // Each request is handled by the NetworkErrorHandler
            tasks[i] = apiManager.RunRequestWithErrorHandlingAsync(endpoints[i]);
        }

        try
        {
            // Wait for all requests to complete
            var results = await UniTask.WhenAll(tasks);
            Debug.Log("All requests completed successfully:");
            foreach (var result in results)
            {
                Debug.Log(result);
            }
        }
        catch (Exception e)
        {
            // If all retries fail, handle the exception here
            Debug.LogError($"Some requests ultimately failed: {e.Message}");
        }
    }
}

Hi,
Have you tried the latest version 1.5.3?
If that doesn’t work could you please file a bug report? Unity QA: Building quality with passion