Dramatically annoying crash at std::thread and std::mutex destructors on Android

It is a long story that started here about 2 years ago. My first understanding was that it is a QT bug that was not fixed within this 2 years, of course (as a rule or usually), but a couple weeks ago I noticed that a similar crash happens on Samsung Galaxy devices:

#00  pc 000000000005fcb2  /apex/com.android.runtime/lib/bionic/libc.so (abort+166)
#00  pc 00000000000a9393  /apex/com.android.runtime/lib/bionic/libc.so (__fortify_fatal(char const*, ...)+26)
#00  pc 00000000000a8b9d  /apex/com.android.runtime/lib/bionic/libc.so (HandleUsingDestroyedMutex(pthread_mutex_t*, char const*)+20)
#00  pc 00000000000a8a89  /apex/com.android.runtime/lib/bionic/libc.so (pthread_mutex_lock+132)
#00  pc 00000000000a7879  /apex/com.android.runtime/lib/bionic/libc.so (pthread_cond_wait+40)
#00  pc 000000000004de2d  /system/lib/libc++.so (std::__1::condition_variable::wait(std::__1::unique_lock<std::__1::mutex>&)+8)
#00  pc 000000000038a7f1  /system/lib/libhwui.so (android::uirenderer::CommonPool::workerLoop()+60)
#00  pc 000000000038a73f  /system/lib/libhwui.so (void* std::__1::__thread_proxy<std::__1::tuple<std::__1::unique_ptr<std::__1::__thread_struct, std::__1::default_delete<std::__1::__thread_struct> >, android::uirenderer::CommonPool::CommonPool()::$_0> >(void*)+94)
#00  pc 00000000000a8147  /apex/com.android.runtime/lib/bionic/libc.so (__pthread_start(void*)+20)
#00  pc 0000000000061467  /apex/com.android.runtime/lib/bionic/libc.so (__start_thread+30)

or

#00  pc 0000000000083134  /apex/com.android.runtime/lib64/bionic/libc.so (abort+160)
#00  pc 00000000000e3ea4  /apex/com.android.runtime/lib64/bionic/libc.so (__fortify_fatal(char const*, ...)+116)
#00  pc 00000000000e3594  /apex/com.android.runtime/lib64/bionic/libc.so (HandleUsingDestroyedMutex(pthread_mutex_t*, char const*)+52)
#00  pc 00000000000e3e1c  /apex/com.android.runtime/lib64/bionic/libc.so (pthread_mutex_destroy+140)
#00  pc 00000000000afbd8  /system/lib64/libc++.so (std::__1::mutex::~mutex()+8)
#00  pc 00000000000e56d0  /apex/com.android.runtime/lib64/bionic/libc.so (__cxa_finalize+212)
#00  pc 00000000000e1060  /apex/com.android.runtime/lib64/bionic/libc.so (exit+24)
#00  pc 000000000003ed78  /data/app/my.app-128oJcRcQb3-falfSayklQ==/lib/arm64/libplugins_platforms_qtforandroid_arm64-v8a.so (startQtApplication(_JNIEnv*, _jclass*)) (SourceCode: E:/repos/qt-everywhere-src-6.2.2/qtbase/src/plugins/platforms/android/androidjnimain.cpp:567)
#00  pc 000000000002fca0  /data/app/my.app-128oJcRcQb3-falfSayklQ==/oat/arm64/base.odex (art_jni_trampoline+144)
#00  pc 0000000000055398  /data/app/my.app-128oJcRcQb3-falfSayklQ==/oat/arm64/base.odex (org.qtproject.qt.android.QtNative$7.run+40)
#00  pc 0000000000058580  /data/app/my.app-128oJcRcQb3-falfSayklQ==/oat/arm64/base.odex (org.qtproject.qt.android.QtThread$1.run+576)
#00  pc 00000000001a5568  /system/framework/arm64/boot.oat (java.lang.Thread.run+72)
#00  pc 0000000000137334  /apex/com.android.runtime/lib64/libart.so (art_quick_invoke_stub+548)
#00  pc 000000000014606c  /apex/com.android.runtime/lib64/libart.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+244)
#00  pc 00000000004ab710  /apex/com.android.runtime/lib64/libart.so (art::(anonymous namespace)::InvokeWithArgArray(art::ScopedObjectAccessAlreadyRunnable const&, art::ArtMethod*, art::(anonymous namespace)::ArgArray*, art::JValue*, char const*)+104)
#00  pc 00000000004ac7a4  /apex/com.android.runtime/lib64/libart.so (art::InvokeVirtualOrInterfaceWithJValues(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, _jmethodID*, jvalue const*)+416)
#00  pc 00000000004ecab8  /apex/com.android.runtime/lib64/libart.so (art::Thread::CreateCallback(void*)+1176)
#00  pc 00000000000e28c0  /apex/com.android.runtime/lib64/bionic/libc.so (__pthread_start(void*)+36)
#00  pc 000000000008503c  /apex/com.android.runtime/lib64/bionic/libc.so (__start_thread+64)

or

#00  pc 0000000000083134  /apex/com.android.runtime/lib64/bionic/libc.so (abort+160)
#00  pc 00000000000e3ea4  /apex/com.android.runtime/lib64/bionic/libc.so (__fortify_fatal(char const*, ...)+116)
#00  pc 00000000000e3594  /apex/com.android.runtime/lib64/bionic/libc.so (HandleUsingDestroyedMutex(pthread_mutex_t*, char const*)+52)
#00  pc 00000000000e33f4  /apex/com.android.runtime/lib64/bionic/libc.so (pthread_mutex_lock+164)
#00  pc 00000000001ad59c  /system/lib64/libandroid_runtime.so (android_media_AudioTrack_write_native_bytes(_JNIEnv*, _jobject*, _jobject*, int, int, int, unsigned char)+72)
#00  pc 00000000002e7ad8  /system/framework/arm64/boot-framework.oat (art_jni_trampoline+184)
#00  pc 00000000020199fc  /memfd:/jit-cache (android.media.AudioTrack.write+380)
#00  pc 000000000201a7b0  /memfd:/jit-cache (com.google.android.gms.ads.exoplayer3.audio.s.m+304)
#00  pc 000000000201d470  /memfd:/jit-cache (com.google.android.gms.ads.exoplayer3.audio.s.n+192)
#00  pc 000000000201cf28  /memfd:/jit-cache (com.google.android.gms.ads.exoplayer3.audio.v.M+2936)
#00  pc 0000000002018fe0  /memfd:/jit-cache (com.google.android.gms.ads.exoplayer3.mediacodec.c.z+1664)
#00  pc 000000000200cff8  /memfd:/jit-cache (com.google.android.gms.ads.exoplayer3.n.handleMessage+10360)
#00  pc 0000000000756028  /system/framework/arm64/boot-framework.oat (android.os.Handler.dispatchMessage+136)
#00  pc 00000000007595f8  /system/framework/arm64/boot-framework.oat (android.os.Looper.loop+1448)
#00  pc 0000000000758034  /system/framework/arm64/boot-framework.oat (android.os.HandlerThread.run+548)
#00  pc 0000000000137334  /apex/com.android.runtime/lib64/libart.so (art_quick_invoke_stub+548)
#00  pc 000000000014606c  /apex/com.android.runtime/lib64/libart.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+244)
#00  pc 00000000004ab8e0  /apex/com.android.runtime/lib64/libart.so (art::(anonymous namespace)::InvokeWithArgArray(art::ScopedObjectAccessAlreadyRunnable const&, art::ArtMethod*, art::(anonymous namespace)::ArgArray*, art::JValue*, char const*)+104)
#00  pc 00000000004ac974  /apex/com.android.runtime/lib64/libart.so (art::InvokeVirtualOrInterfaceWithJValues(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, _jmethodID*, jvalue const*)+416)
#00  pc 00000000004ecd08  /apex/com.android.runtime/lib64/libart.so (art::Thread::CreateCallback(void*)+1176)
#00  pc 00000000000e28c0  /apex/com.android.runtime/lib64/bionic/libc.so (__pthread_start(void*)+36)
#00  pc 000000000008503c  /apex/com.android.runtime/lib64/bionic/libc.so (__start_thread+64)

all the stack traces have either pthread_mutex_lock or pthread_mutex_destroy and as far as I can guess it can be an attempt to lock or destroy an already destroyed mutex. This happens on the following devices:

plus Huawei that I fixed by calling my quitGracefully() Java method:

public void quitGracefully()
{
    String man = android.os.Build.MANUFACTURER.toUpperCase();

    Log.d(TAG, String.format("quitGracefully() in Java, MANUFACTURER: '%s'", man));

    if (man.contains("HUAWEI"))
    {
        finishAffinity();
        System.exit(0);
    }
}

from aboutToQuit handler that is called before global C++ objects are destroyed.

I am not sure if this relate to QT or not, but I’ll try the next version of my epic workaround:

public void quitGracefully()
{
    String man = android.os.Build.MANUFACTURER.toUpperCase();

    Log.d(TAG, String.format("quitGracefully() in Java, MANUFACTURER: '%s'", man));

    boolean fix = man.contains("HUAWEI");

    if (!fix)
    {
        if (man.contains("SAMSUNG"))
        {
            String model = android.os.Build.MODEL;

            Log.d(TAG, String.format("quitGracefully() in Java, MODEL: '%s'", model));

            if (man.contains("GALAXY"))
            {
                fix = true;
            }
        }
    }

    if (fix)
    {
        finishAffinity();
        System.exit(0);
    }
}

There is the following code in qtbase/src/plugins/platforms/android/androidjnimain.cpp:

if (!qEnvironmentVariableIsSet("QT_ANDROID_NO_EXIT_CALL"))
    exit(ret);

and theoretically the workaround can be

qputenv("QT_ANDROID_NO_EXIT_CALL", "1");

it prevents the destructors of global objects from being called:

namespace
{
    class Global
    {
    public:

        ~Global()
        {
            qDebug() << "Global destructor.";
        }
    };

    Global g;
}

Leave a Reply

Your email address will not be published. Required fields are marked *